import { _decorator, animation, CapsuleCharacterController, CharacterControllerContact, clamp, Component, Node, ParticleSystem, PhysicsSystem, quat, Quat, toRadian, tween, v3, Vec3 } from 'cc';
import { GamerBackPack } from './GamerBackpack';
import { Heal } from './Items/Heal';
import Events from '../../Scripts/Framework/Utils/Events';
import { EventName } from '../../Scripts/Framework/Const/EventName';
import { Gun } from './Items/Gun';
import { Ammo } from './Items/Ammo';
import { UIManager } from '../../Scripts/Framework/Managers/UIManager';
import { Weapon } from './Items/Weapon';
import { ItemBox } from './ItemBox';
import { BodyTrigger } from './BodyTrigger';
import { AudioManager } from './Managers/AudioManager';
import { PoisonManager } from './Managers/PoisonManager';
import { GameUI } from './GameUI';
import { InGameManager } from './Managers/InGameManager';
const { ccclass, property } = _decorator;
const rotation = new Quat();
const scale = new Vec3(1);

@ccclass('GamerController')
export class GamerController extends Component {

    type: 'PlayerController' | 'EnemyController' | 'RobotController';

    backPack: GamerBackPack;

    @property
    speed = 0;

    @property
    public gravityValue = -9.81;

    @property
    public jumpSpeed = 5;

    @property
    public linearDamping = 0.9;

    @property
    public pushPower = 4;

    characterController: CapsuleCharacterController;
    animationController: animation.AnimationController;

    _control_z = 0;
    public get Control_z(): number {
        return this._control_z;
    }
    public set Control_z(value: number) {
        this._control_z = clamp(value, this.healTime > 0 ? -0.5 : -1, this.healTime > 0 ? 0.5 : 2);
    }

    _control_x = 0;
    public get Control_x(): number {
        return this._control_x;
    }
    public set Control_x(value: number) {
        this._control_x = clamp(value, this.healTime > 0 ? -0.5 : -1, this.healTime > 0 ? 0.5 : 1);
    }

    deltaNor = v3();
    _movement = new Vec3(0, 0, 0);
    _grounded = false;
    _playerVelocity = new Vec3(0, 0, 0);
    _moveY = v3();
    _doJump = false;
    isShoot = false;
    isDead = false;
    isHeal = false;
    isRun = false;
    isInit = false;
    isParachute = false;
    _lastIsGround: boolean = true;
    _lastMoveX = 0;
    _lastMoveZ = 0;
    curSpread = 0;
    healTime = 0;
    parachuteSpeed = 2;
    _updateCount: number = 0;
    _lodLevel: number = 0; // 0: 高细节, 1: 中细节, 2: 低细节
    healItem: Heal;
    headwears: Node;
    wings: Node;
    beibao: Node;

    hp = 100;
    public get Hp(): number { return this.hp; }
    public set Hp(value: number) {
        if (this.isDead) return;
        this.hp = clamp(value, 0, 100);
        if (this.hp == 0) this.Die();
    }

    healCharge = 0;
    core: Node;

    protected onLoad(): void {
        this.characterController = this.getComponent(CapsuleCharacterController);
        // this.characterController.on('onControllerColliderHit', this.onControllerColliderHit, this);
        this.characterController.on('onControllerTriggerEnter', this.onControllerTriggerEnter, this);
        this.characterController.on('onControllerTriggerExit', this.onControllerTriggerExit, this);
        this.backPack = this.getComponent(GamerBackPack);
        this.Hp = 100;
        this.schedule(this.HealSchedule, 1);
        this.core = this.node.children[1];
        this.schedule(this.CheckPoison, 1);
    }

    start() {

    }

    update(deltaTime: number) {
        this.CheckShoot();
    }

    InitModel() {

    }

    Move(deltaTime: number) {
        if (!this.isInit) return;
        if (!this.characterController)
            return;

        // this.animationController.setValue("MoveX", this.deltaNor.x);
        // this.animationController.setValue("MoveY", this.deltaNor.y);
        // 设置动画参数 - 只有在值变化时更新
        if (Math.abs(this.deltaNor.x - this._lastMoveX) > 0.01 ||
            Math.abs(this.deltaNor.y - this._lastMoveZ) > 0.01) {
            this.animationController.setValue("MoveX", this.deltaNor.x);
            this.animationController.setValue("MoveY", this.deltaNor.y);
            this._lastMoveX = this.deltaNor.x;
            this._lastMoveZ = this.deltaNor.y;
        }
        // deltaTime = PhysicsSystem.instance.fixedTimeStep;

        if (this.isDead) {
            this._playerVelocity.z = 0;
            this._playerVelocity.x = 0;
            return;
        }
        else {
            if (this._grounded) {
                if (this.type == 'PlayerController') {
                    if (this.isParachute) {
                        this.isParachute = false;
                        this.animationController.setValue("IsParachute", false);
                        let controlUI: any = InGameManager.Instance.controlUI.getComponent("ControlUI");
                        controlUI.Land();
                        this.node.children[0].setRotationFromEuler(v3(0, 180));
                        this.wings.active = true;
                        // let pos = this.beibao.getPosition();
                        // this.beibao.setParent(InGameManager.Instance.gamers, true);
                        // tween(this.beibao)
                        //     .by(2, { position: v3(0, -10) })
                        //     .call(() => {
                        //         this.beibao.active = false;
                        //         this.beibao.setParent(this.wings.parent, true);
                        //         this.beibao.setPosition(pos);
                        //         this.beibao.children[1].setScale(Vec3.ZERO);
                        //     })
                        //     .start();
                    }
                }
                if (this._doJump) {
                    this._playerVelocity.y += this.jumpSpeed;
                    this._doJump = false;
                }
                else {
                    //control impulse
                    this._playerVelocity.z += -this.Control_z * this.speed;
                    this._playerVelocity.x += this.Control_x * this.speed;

                    // damping
                    this._playerVelocity.x *= this.linearDamping;
                    this._playerVelocity.z *= this.linearDamping;

                    //grounded
                    this._playerVelocity.y = 0;
                }
            }
            else {
                // Gravity
                if (this.isParachute) {
                    this._playerVelocity.z += (this.Control_z - 2) * this.speed;
                    this._playerVelocity.x += this.Control_x * this.speed;
                    this._playerVelocity.x *= this.linearDamping;
                    this._playerVelocity.z *= this.linearDamping;
                    this.parachuteSpeed = 2 + this.Control_z;
                    this._playerVelocity.y = this.parachuteSpeed * this.gravityValue;
                    if (this.node.getWorldPosition().y < 200) {
                        this._playerVelocity.y /= 2;
                        if (!this.animationController.getValue("IsParachuteOpen")) this.animationController.setValue("IsParachuteOpen", true);
                    }
                    if (this.type == 'PlayerController') {
                        let euler = this.node.children[0].eulerAngles.clone();
                        let num = this.Control_x > 0.2 ? 1 : this.Control_x < -0.2 ? -1 : 0;
                        let speed = 10;
                        if (num != 0) euler.z += num * speed * deltaTime;
                        else if (euler.z > 0) euler.z -= speed * deltaTime;
                        else if (euler.z < 0) euler.z += speed * deltaTime;
                        euler.z = clamp(euler.z, -15, 15);
                        if (this.node.getWorldPosition().y < 200) {
                            num = this.Control_z > 0.2 ? 1 : this.Control_z < -0.2 ? -1 : 0;
                            if (num != 0) euler.x += num * speed * deltaTime;
                            else if (euler.x > 0) euler.x -= speed * deltaTime;
                            else if (euler.x < 0) euler.x += speed * deltaTime;
                            euler.x = clamp(euler.x, -15, 15);
                        }
                        this.node.children[0].setRotationFromEuler(euler);
                    }
                }
                else this._playerVelocity.y += this.gravityValue * deltaTime;
            }
        }
        let radian = toRadian(this.node.eulerAngles.y);
        Vec3.multiplyScalar(this._movement, this._playerVelocity, deltaTime);
        // 旋转移动向量 - 优化旋转计算
        if (this._movement.lengthSqr() > 0.0001) { // 只有有移动时才旋转
            this._moveY.y = this._movement.y;
            Vec3.rotateY(this._movement, this._movement, this._moveY, radian);
        }
        this.characterController!.move(this._movement);
        if (this.type == 'PlayerController') {
            let move = this._movement.clone();
            move.y = 0;
            InGameManager.Instance.endData[4] += move.length() / 10;
        }
        if (this.characterController.isGrounded) this._playerVelocity.y = 0;
        this._grounded = this.characterController!.isGrounded;
        // 只有当地面状态变化时才更新动画参数
        if (this._grounded !== this._lastIsGround) {
            this.animationController.setValue("IsGround", this._grounded);
            this._lastIsGround = this._grounded;
        }
    }

    updateLOD() {
        // 根据与摄像头的距离决定LOD级别
        // 需要获取摄像头位置，这里假设有全局的摄像头访问方式
        const cameraPos = InGameManager.Instance.cameraNode.worldPosition;
        const myPos = this.node.worldPosition;
        const distance = Vec3.distance(cameraPos, myPos);

        if (distance < 50) this._lodLevel = 0; // 高细节
        else if (distance < 100) this._lodLevel = 1; // 中细节
        else if (distance < 200) this._lodLevel = 2; // 低细节
        else if (distance < 400) this._lodLevel = 3;
        else if (distance < 800) this._lodLevel = 4;
        else this._lodLevel = 5;
    }

    Jump() {
        if (this.isDead) return;
        if (this._grounded) {
            this._doJump = true;
            this.animationController.setValue("IsJump", this._doJump);
        }
    }

    TryReload() {
        let pack = this.backPack;
        let weapon = pack.CurWeapon;
        if (weapon instanceof Gun) {
            if (weapon.curAmmo == weapon.maxAmmo) return UIManager.ShowTip("弹药已满！");
            let type = weapon.type;
            let ammo = pack.items.find((value, index, array) => { if (value instanceof Ammo && value.type == type) return value; });
            if (ammo && ammo.num > 0) this.Reload();
            else {
                AudioManager.Instance.PlayNoAmmo();
                if (this.type == 'PlayerController') UIManager.ShowTip("弹药耗尽！");
            }
        }
    }

    Reload() {
        if (this.isDead) return;
        if (this.animationController.getValue("IsReload")) return;
        this.animationController.setValue("IsReload", true);
        this.animationController.setValue("IsHeal", false);
        let vol = Math.max(1 - (Vec3.distance(this.node.getWorldPosition(), InGameManager.Instance.cameraNode.getWorldPosition()) / 100), 0);
        AudioManager.Instance.PlayReload(vol);
    }

    ReloadFinish() {
        let pack = this.backPack;
        let weapon = pack.CurWeapon;
        if (weapon instanceof Gun) {
            if (this.type == 'PlayerController') {
                let type = weapon.type;
                let cur = weapon.curAmmo;
                let max = weapon.maxAmmo;
                let ammos = pack.items.filter((value, index, array) => { if (value instanceof Ammo && value.type == type) return value; });
                ammos.sort((a, b) => { return b.num - a.num; });
                let ammoNum = 0;
                while (cur + ammoNum < max && ammos.length > 0) {
                    let ammo = ammos[0];
                    if (cur + ammoNum + ammo.num >= max) {
                        ammo.num -= max - cur - ammoNum;
                        ammoNum = max - cur;
                        if (ammo.num == 0) {
                            ammos.splice(0, 1);
                            pack.DestroyItem(ammo);
                        }
                    }
                    else {
                        ammoNum += ammo.num;
                        ammos.splice(0, 1);
                        pack.DestroyItem(ammo);
                    }
                }
                weapon.curAmmo += ammoNum;
                Events.emit(EventName.PlayerWeaponData, weapon.type);
            }
            else weapon.curAmmo = weapon.maxAmmo;
        }
    }

    Aim(bool: boolean) {
        if (this.isDead) return;
        this.animationController.setValue("IsAim", bool);
    }

    ShootStart() {
        if (this.isDead) return;
        this.animationController.setValue("IsShoot", true);
        // this.animationController.setValue("IsCD", false);
    }

    Shoot() {
        if (GameUI.Instance.isFinish) return;
        if (this.isDead || !this.animationController.getValue("IsShoot")) return;
        let weapon = this.backPack.CurWeapon;
        if (weapon instanceof Gun) {
            if (weapon.curAmmo == 0) return this.TryReload();
            let type = weapon.type;
            let vol = Math.max(1 - (Vec3.distance(this.node.getWorldPosition(), InGameManager.Instance.cameraNode.getWorldPosition()) / 100), 0);
            AudioManager.Instance.PlayShoot(type, vol);
            let particles = this.backPack.weaponSlots[4].children[0]?.children[0];
            if (particles) {
                for (let i = 0; i < particles.children.length; i++) {
                    const element = particles.children[i].getComponent(ParticleSystem);
                    element.stop();
                    element.play();
                }
            }
            let bullets = weapon.bullet;
            for (let i = 0; i < bullets; i++) { this.ShootBullet(); }
            this.curSpread = Math.min(this.curSpread + weapon.recoil, weapon.maxSpread);
            weapon.curAmmo--;
            if (weapon.curAmmo == 0) this.TryReload();
            if (this.type == 'PlayerController') Events.emit(EventName.PlayerWeaponData, weapon);
        }
    }

    ShootBullet() {

    }

    // 替代方法：使用更简单的向量操作（性能更好）
    getRandomShootDirectionSimple(cameraForward: Vec3, cameraUp: Vec3, cameraRight: Vec3): Vec3 {
        // 1. 获取基础方向
        const baseDirection = cameraForward.clone().normalize();

        // 2. 生成随机偏移向量
        const randomOffset = this.generateRandomOffsetVector();

        // 3. 将偏移向量转换到摄像机空间
        const worldOffset = v3();
        Vec3.scaleAndAdd(worldOffset, worldOffset, cameraRight, randomOffset.x);
        Vec3.scaleAndAdd(worldOffset, worldOffset, cameraUp, randomOffset.y);

        // 4. 添加到基础方向
        const result = v3();
        Vec3.add(result, baseDirection, worldOffset);

        // 6. 归一化并返回
        return result.normalize();
    }

    // 生成随机偏移向量
    private generateRandomOffsetVector(): Vec3 {
        // 计算最大偏移量（基于当前散布角度）
        const maxOffset = Math.tan(toRadian(this.curSpread));

        // 生成圆内的随机点（确保均匀分布）
        const r = Math.sqrt(Math.random()) * maxOffset;
        const theta = Math.random() * 2 * Math.PI;

        return v3(
            r * Math.cos(theta),
            r * Math.sin(theta),
            0
        );
    }

    CheckShoot() {
        let weapon = this.backPack.CurWeapon;
        if (weapon instanceof Gun) {
            this.curSpread = clamp(this.curSpread - weapon.recoilRecovery, weapon.spread, weapon.maxSpread);
        }
        if (this.type === 'PlayerController') Events.emit(EventName.PlayerShootCheck);
    }

    ShootEnd() {
        this.animationController.setValue("IsShoot", false);
    }

    Die() {
        if (this.isDead) return;
        this.animationController.setValue("IsDead", true);
        this.isDead = true;
        Events.emit(EventName.GamerDie);
        if (this.type == 'PlayerController') GameUI.Instance.Fail();
        else InGameManager.Instance.aliveEnemy--;
    }

    WeaponSwitch(weapon: Weapon) {
        if (this.isDead) return;
        if (this.animationController.getValue("IsChooseWeapon")) return;
        this.animationController.setValue("IsChooseWeapon", true);
        this.animationController.setValue("IsReload", false);
        this.backPack.switchWeapon = weapon;
    }

    CheckAmmo() {
        if (this.isDead) return;
        let gun = this.backPack.CurWeapon as Gun;
        if (gun.curAmmo == 0) {
            let ammo = this.backPack.items.find((value, index, array) => { if (value instanceof Ammo && value.type == gun.type) return value; });
            if (ammo && ammo.num > 0) this.Reload();
            else {
                //弹药耗尽
                if (this.type === 'PlayerController') UIManager.ShowTip("弹药耗尽！");
            }
        }
    }

    Heal(bool = true) {
        if (this.isDead) return;
        this.animationController.setValue("IsHeal", bool);
        if (bool) this.animationController.setValue("IsReload", false);
    }

    HealSchedule() {
        if (this.healCharge > 0 && !this.isDead) {
            this.healCharge--;
            this.Hp++;
        }
    }

    _InHeal(dt: number) {
        if (this.isDead) return (this.type === 'PlayerController' && Events.emit(EventName.PlayerHealCompleted));
        if (!this.healItem) return;
        if (this.healTime > 0) {
            if (!this.isHeal) {
                this.isHeal = true;
                this.Control_x = this.Control_x;
                this.Control_z = this.Control_z;
                this.deltaNor.set(this.Control_x, this.Control_z);
                if (this.type === 'PlayerController') Events.emit(EventName.PlayerHealStart);
            }
            this.healTime -= dt;
            if (this.healTime <= 0) {
                this.isHeal = false;
                this.healTime = 0;
                this.healItem.num--;
                if (this.healItem.type == 0) this.Hp += this.healItem.heal;
                else this.healCharge += this.healItem.heal;
                if (this.healItem.num == 0) this.backPack.DestroyItem(this.healItem);
                this.healItem = null;
                this.Heal(false);
                if (this.isRun) {
                    this.Control_x = 0;
                    this.Control_z = 1.5;
                    this.deltaNor.set(this.Control_x, this.Control_z);
                }
                if (this.type === 'PlayerController') Events.emit(EventName.PlayerHealCompleted);
            }
            else if (this.type === 'PlayerController') Events.emit(EventName.PlayerHealCheck);
        }
    }

    Damage(part: BodyTrigger, weapon: Weapon, source: GamerController) {
        if (this.Hp == 0) return;
        let armors = this.backPack.armors;
        let damage = 10;
        if (weapon) damage = weapon.damage;
        if (part.type == 0) {
            if (source.type == 'PlayerController') InGameManager.Instance.endData[1]++;
            if (weapon instanceof Gun) damage *= weapon.headShotMultiply;
            if (armors[0] && armors[0].durability > 0) {
                armors[0].durability = Math.max(0, armors[0].durability - damage * weapon.armorExpose);
                damage *= 1 - armors[0].def;
                if (this.type == 'PlayerController') Events.emit(EventName.PlayerArmorCheck);
            }
        }
        else if (part.type == 1) {
            if (armors[1] && armors[1].durability > 0) {
                armors[1].durability = Math.max(0, armors[1].durability - damage * weapon.armorExpose);
                damage *= 1 - armors[1].def;
                if (this.type == 'PlayerController') Events.emit(EventName.PlayerArmorCheck);
            }
        }
        if (source.type == 'PlayerController') GameUI.Instance.ShowDmg(damage, part.node.getWorldPosition());
        if (this.type == 'PlayerController') {
            damage *= 0.05;
            Events.emit(EventName.PlayerHurt, source);
        }
        this.Hp -= damage;
        if (this.isDead && source.type == 'PlayerController') Events.emit(EventName.PlayerKill);
        let pos = this.node.getWorldPosition();
        let dis = Vec3.distance(pos, source.node.getWorldPosition(pos));
        AudioManager.Instance.PlayHurt(Math.max(1 - dis / 100, 0));
    }

    CheckPoison() {
        if (GameUI.Instance.isFinish) return;
        // console.log(this.Hp);
        // console.log(Math.max(PoisonManager.Instance.round, 1));
        if (!PoisonManager.Instance.CheckInPoison(this.node.getWorldPosition())) this.Hp -= Math.pow(2, PoisonManager.Instance.round);
    }

    MeleeEffectStart() {
        let effect = this.backPack.weaponSlots[4].children[0]?.children[0]?.getComponent(ParticleSystem);
        effect?.play();
    }

    MeleeEffectEnd() {
        let effect = this.backPack.weaponSlots[4].children[0]?.children[0]?.getComponent(ParticleSystem);
        effect?.stop();
    }

    OpenP() {
        tween(this.beibao.children[1])
            .to(0.5, { scale: Vec3.ONE })
            .start();
    }

    onControllerColliderHit(hit: CharacterControllerContact) {
        return;
        // console.log('Test onColliderHit');
        // console.log('selfCCT ', selfCCT.node.name, ' hitCollider ', hitCollider.node.name);
        // console.log('character velocity ', selfCCT.getVelocity());
        //selfCCT.detectCollisions = false;

        Quat.rotationTo(rotation, Vec3.UNIT_Y, hit.worldNormal);
        scale.set(0.05, 1, 0.05);

        const body = hit.collider?.attachedRigidBody;
        // no rigidbody
        if (body == null || body.isKinematic) {
            return;
        }

        // We dont want to push objects below us
        if (hit.motionDirection.y < -0.1) {
            return;
        }

        // Calculate push direction from move direction,
        // we only push objects to the sides never up and down
        const pushDir = new Vec3(hit.motionDirection.x, 0, hit.motionDirection.z);

        // If you know how fast your character is trying to move,
        // then you can also multiply the push velocity by that.

        // Apply the push
        Vec3.multiplyScalar(pushDir, pushDir, this.pushPower);
        body.setLinearVelocity(pushDir);
    }

    onControllerTriggerEnter(hit: CharacterControllerContact) {
        let group = hit.collider.attachedRigidBody.group;
        if (group == 1 << 3) { this.backPack.ContainBox = hit.collider.node.parent.getComponent(ItemBox); }
    }

    onControllerTriggerExit(hit: CharacterControllerContact) {
        let group = hit.collider.attachedRigidBody.group;
        if (group == 1 << 3 && this.backPack.ContainBox == hit.collider.node.parent.getComponent(ItemBox)) { this.backPack.ContainBox = null; }
    }
}


